﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Globalization;
using Elf.Common;
using Castle.MicroKernel.Registration;
using Elf.ServiceModel.Interfaces;

namespace Elf.ServiceModel
{
    public class ServiceClient<TInterface> where TInterface : class
    {
        #region Singleton
        /// <summary>
        /// Prevents a default instance of the <see cref="ServiceClient&lt;TInterface&gt;.ServiceClient"/> class from being created.
        /// </summary>
        ServiceClient()
        {
            // Check if not allready registered WCF Interceptor
            var components = DependencyFactory.Instance.Container.Kernel.GetAssignableHandlers(typeof(IWcfInterceptor<TInterface>));

            if (components.Length == 0)
            {
                DependencyFactory.Instance.Container.Register(Component.For<IWcfInterceptor<TInterface>>().ImplementedBy<WcfInterceptor<TInterface>>());
            }
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")]
        public static ServiceClient<TInterface> Instance
        {
            get
            {
                return Nested.instance;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Singleton implementation")]
        class Nested
        {
            // Explicit static constructor to tell C# compiler
            // not to mark type as beforefieldinit
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Singleton implementation")]
            static Nested()
            {
            }

            internal static readonly ServiceClient<TInterface> instance = new ServiceClient<TInterface>();
        }
        #endregion

        #region Fields
        TInterface _proxy = null;
        ProxyGenerator _generator = new ProxyGenerator();
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the proxy by convention: "[Interface]_Endpoint", for instance: "IService_Endpoint".
        /// </summary>
        /// <value>
        /// The proxy.
        /// </value>
        public TInterface Proxy
        {
            get
            {
                if (_proxy == null)
                {
                    return CreateProxy();
                }
                else
                {
                    return _proxy;
                }
            }
        }
        #endregion

        #region Operations
        /// <summary>
        /// Creates the proxy based on convention: "[Interface]_Endpoint", for instance: "IService_Endpoint".
        /// </summary>
        /// <returns></returns>
        public TInterface CreateProxy()
        {
            _proxy = _generator.CreateInterfaceProxyWithoutTarget<TInterface>(DependencyFactory.Instance.Container.Resolve<IWcfInterceptor<TInterface>>(new { endpointConfigName = string.Format(CultureInfo.CurrentUICulture, "{0}_Endpoint", typeof(TInterface).Name) }));
            return _proxy;
        }

        /// <summary>
        /// Creates the proxy based on the endpoint configuration name.
        /// </summary>
        /// <param name="endpointConfigName">Name of the endpoint config.</param>
        /// <returns></returns>
        public TInterface CreateProxy(string endpointConfigName)
        {
            _proxy = _generator.CreateInterfaceProxyWithoutTarget<TInterface>(DependencyFactory.Instance.Container.Resolve<IWcfInterceptor<TInterface>>(endpointConfigName));
            return _proxy;
        }

        /// <summary>
        /// Creates the proxy based on binding and address.
        /// </summary>
        /// <param name="binding">The binding.</param>
        /// <param name="address">The address.</param>
        /// <returns></returns>
        public TInterface CreateProxy(Binding binding, EndpointAddress address)
        {
            _proxy = _generator.CreateInterfaceProxyWithoutTarget<TInterface>(DependencyFactory.Instance.Container.Resolve<IWcfInterceptor<TInterface>>(new { binding = binding, address = address }));
            return _proxy;
        }
        #endregion

    }
}
